package core;

import static org.junit.Assert.*;

import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.Set;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

public class ProductManagerTests {

	@BeforeClass
	public static void setUpBeforeClass() throws Exception 
	{
	}

	@AfterClass
	public static void tearDownAfterClass() throws Exception 
	{
		System.out.println("Cleaning up ProductManager");
		
		ProductManager pm = ProductManager.getInstance();
		
		List<Product> list = pm.getAllProducts();
		
		if (list.size() != 0)
		{
			Iterator it = list.iterator();
			
			while (it.hasNext())
			{
				pm.deleteProduct((Product)it.next());
			}
		}
		
		assertTrue (pm.getAllProducts().size() == 0);
		
		System.out.println("     DONE.");
	}

	@Before
	public void setUp() throws Exception 
	{
	}

	@After
	public void tearDown() throws Exception 
	{
	}
	
	@Test 
	public void TestBarcode()
	{
		Barcode b1 = new Barcode("382058274391");
		Barcode b2 = new Barcode("382058274391");
		
		//assertTrue (b1 == b2);
		assertTrue (b1.equals(b2));
		assertTrue (b1.hashCode() == b2.hashCode());
		
		String s1 = "12345";
		String s2 = "12";
		assertFalse(s1.hashCode() == s2.hashCode());
		assertFalse(s1.compareTo(s2) == 0);
		
		s2 = s2 + "345";
		
		assertTrue(s1.compareTo(s2) == 0);
		assertTrue(s1.hashCode() == s2.hashCode());
	}
	
	@Test
	public void TestGetInstance()
	{
		ProductManager pm = ProductManager.getInstance();
		
		ProductManager pm1 = ProductManager.getInstance();
		
		assertTrue (pm == pm1);
		
		pm.addProduct(ProductManager.createProduct(new Amount(Unit.count, 2),
				new Barcode("123456789012"), "DO", 0, 0));
		
		ProductManager pm2 = ProductManager.getInstance();
		
		Product exist = pm2.getProduct(new Barcode("123456789012"));
		
		assertTrue (exist.getBarcodeString().compareTo("123456789012") == 0);
		assertTrue (exist.getDescription().compareTo("DO") == 0);
		assertTrue (exist.getShelfLife() == 0);
		assertTrue (exist.getAmount().getUnit() == Unit.count);
		assertTrue (exist.getAmount().getSize() == 2);
		
		assertTrue (pm.deleteProduct(pm.getProduct(new Barcode("123456789012"))));
		
		//assertTrue (pm.getAllProducts().size() == 0);
	}
	
	public Product createRandomProduct()
	{
		Random randomGenerator = new Random();
		
		int amountCount = randomGenerator.nextInt(20);
		
		String barcode = "";
		for (int i = 0; i < 12; i++)
		{
			barcode += randomGenerator.nextInt(10);
		}
		
		String description = "This is a description " + randomGenerator.nextInt();
		
		int shelfLife = randomGenerator.nextInt(256);
		int tms = randomGenerator.nextInt(100);
		
		return ProductManager.createProduct(new Amount(Unit.count, amountCount), 
				new Barcode(barcode), description, shelfLife, tms);
		
	}
	
	@Test
	public void testCanAdd()
	{
		ProductManager pm = ProductManager.getInstance();
		Product p1 = createRandomProduct();
		
		assertTrue (p1 != null);
		assertTrue (pm.canAdd(p1));
		assertTrue (pm.canAdd(p1));
		
		pm.addProduct(p1);
		
		assertFalse(pm.canAdd(p1));
		
		Product p2 = createRandomProduct();
		
		assertTrue(pm.canAdd(p2));
		assertTrue(pm.addProduct(p2));
		assertFalse(pm.canAdd(p2));
		
		p2.setBarCode(p2.getBarCode());
		
		assertFalse(pm.canAdd(p2));
		assertFalse(pm.canAdd(p1));
		
		//clean up remove what we added
		assertTrue(pm.deleteProduct(p1));
		assertTrue(pm.deleteProduct(p2));
		
		assertTrue(pm.canAdd(p1));
		assertTrue(pm.canAdd(p2));
	}
	
	@Test
	public void testAddProduct()
	{
		ProductManager pm = ProductManager.getInstance();
		Product p1 = createRandomProduct();
		
		assertTrue (pm.addProduct(p1));
		assertTrue (pm.getProduct(p1.getBarCode()) != null);
		
		Product p2 = pm.getProduct(p1.getBarCode());
		
		assertTrue (p2.getBarCode().equals(p1.getBarCode()));
		assertTrue (p2.getDescription().compareTo(p1.getDescription()) == 0);
		
		p2 = createRandomProduct();
		
		assertTrue (pm.addProduct(p2));
		assertTrue (pm.getProduct(p2.getBarCode()) != null);
		
		List<Product> list = pm.getAllProducts();
		assertTrue (list.contains(p1));
		assertTrue (list.contains(p2));
		
		
		//remove Products
		pm.deleteProduct(p1);
		pm.deleteProduct(p2);
		
		assertTrue (pm.getProduct(p1.getBarCode()) == null);
		assertTrue (pm.getProduct(p2.getBarCode()) == null);
	}
	
	@Test
	public void testDeleteProduct()
	{
		ProductManager pm = ProductManager.getInstance();
		Product p1 = createRandomProduct();
		Product p2 = createRandomProduct();
		
		assertTrue(pm.canAdd(p1) && pm.canAdd(p2));
		assertTrue(pm.addProduct(p1) && pm.addProduct(p2));
		
		assertTrue ((pm.getProduct(p1.getBarCode()) != null) && 
					(pm.getProduct(p2.getBarCode()) != null));
		
		assertTrue (pm.deleteProduct(p1));
		assertTrue (pm.getProduct(p1.getBarCode()) == null);
		
		List<Product> list = pm.getAllProducts();
		assertFalse (list.contains(p1));
		assertTrue (list.contains(p2));
		
		Set<ProductContainer> set = pm.getAllProductContainers(p1);
		assertTrue (set == null);
		set = pm.getAllProductContainers(p2);
		assertTrue ((set != null) && (set.size() == 0));
		
		assertTrue (pm.deleteProduct(p2));
		list = pm.getAllProducts();
		assertFalse (list.contains(p2));
		set = pm.getAllProductContainers(p2);
		assertTrue (set == null);
		
		assertTrue ((pm.getProduct(p1.getBarCode()) == null) && 
					(pm.getProduct(p2.getBarCode()) == null));
	}
	
	
	@Test
	public void  testCanEditProduct()
	{
		ProductManager pm = ProductManager.getInstance();
		Product p1 = createRandomProduct();
		Product p2 = createRandomProduct();
		
		assertFalse (p1.getBarCode().equals(p2.getBarCode()));
		assertTrue (pm.addProduct(p1));
		
		assertFalse (pm.canEditProduct(p2, p2));
		assertFalse (pm.canEditProduct(p1, p2));
		
		p2.setBarCode(p1.getBarCode());
		
		assertTrue (p1.getBarCode().equals(p2.getBarCode()));
		assertFalse(p1.getDescription().compareTo(p2.getDescription()) == 0);
		
		assertTrue (pm.canEditProduct(p1, p2));
		assertTrue (pm.deleteProduct(p1));
		assertTrue ((pm.getProduct(p1.getBarCode()) == null) && 
					(pm.getProduct(p2.getBarCode()) == null));
	}
	
	@Test
	public void testEditProduct()
	{
		ProductManager pm = ProductManager.getInstance();
		Product p1 = createRandomProduct();
		Product p2 = createRandomProduct();
		String originalDescription = "my Original Description";
		p1.setDescription("my Original Description");
		
		//System.out.println("P1 " + p1.getDescription());
		//System.out.println("Orig " + originalDescription);
		//System.out.println("P2 " + p2.getDescription());
		
		assertFalse (p1.getBarCode().equals(p2.getBarCode()));
		assertTrue (pm.addProduct(p1));
		
		p2.setBarCode(p1.getBarCode());
		assertTrue (p1.getBarCode().equals(p2.getBarCode()));
		assertFalse(p1.getDescription().compareTo(p2.getDescription()) == 0);
		
		assertTrue (pm.canEditProduct(p1, p2));
		
		//assertTrue (pm.editProduct(p1, p2));
		assertTrue ((pm.getProduct(p2.getBarCode()) != null) && 
					(pm.getProduct(p1.getBarCode()) != null));
		
		//System.out.println("Same " + pm.getProduct(p2.getBarCode()).getDescription());
		//System.out.println("Diff " + originalDescription);
		//System.out.println("Same " + pm.getProduct(p1.getBarCode()).getDescription());
		
		/*assertFalse (pm.getProduct(p2.getBarCode()).getDescription().
						compareTo(originalDescription) == 0);*/
		
		assertTrue (pm.deleteProduct(p1));
		assertFalse(pm.deleteProduct(p2));
		
		p1 = createRandomProduct();
		p2 = createRandomProduct();
		assertFalse (p1.getBarCode().equals(p2.getBarCode()));
		
		try
		{
			pm.editProduct(p1, p1);
			//fail("Didn't catch correctException Modify Test");
		}
		catch (IllegalArgumentException e)
		{
			System.out.println("Caught correct exception Modify Test");
		}
		
		assertTrue(pm.addProduct(p1));
		
		try
		{
			pm.editProduct(p1, p2);
			fail("Didn't catch correctException Modify Test");
		}
		catch (IllegalArgumentException e)
		{
			System.out.println("Caught correct exception Modify Test");
		}
		
		assertTrue(pm.deleteProduct(p1));
		assertTrue(pm.getProduct(p1.getBarCode()) == null);
	}
	
	@Test
	public void testCreateProduct()
	{
		assertTrue (ProductManager.createProduct(new Amount(Unit.count, 2), 
				new Barcode("123456789013"), "My Desc", 1, 4) != null);
		
		try
		{
			ProductManager.createProduct(new Amount(Unit.count, 2), 
					new Barcode("123456789013"), "My Desc", 1, -1);
			fail("Failed to throw exception CreateProduct Test");
		}
		catch (IllegalArgumentException e)
		{
			System.out.println("Caught correct Exception CreateProduct test");
		}
		
		try
		{
			ProductManager.createProduct(new Amount(Unit.count, 2), 
					new Barcode("123456789013"), "My Desc", -1, 1);
			fail("Failed to throw exception CreateProduct Test");
		}
		catch (IllegalArgumentException e)
		{
			System.out.println("Caught correct Exception CreateProduct test");
		}
		
		try
		{
			ProductManager.createProduct(new Amount(Unit.count, 2), 
					new Barcode("123456789013"), "", 1, 1);
			fail("Failed to throw exception CreateProduct Test");
		}
		catch (IllegalArgumentException e)
		{
			System.out.println("Caught correct Exception CreateProduct test");
		}
	}
	
	@Test
	public void testgetProduct()
	{
		ProductManager pm = ProductManager.getInstance();
		Product p1 = createRandomProduct();
		
		assertFalse (pm.getProduct(p1.getBarCode()) != null);
		assertTrue (pm.getProduct(p1.getBarCode()) == null);
		
		assertTrue (pm.canAdd(p1));
		assertTrue (pm.addProduct(p1));
		
		assertTrue (pm.getProduct(p1.getBarCode()) != null);
		assertFalse(pm.getProduct(p1.getBarCode()) == null);
		
		Barcode b1 = new Barcode(p1.getBarcodeString());
		
		assertTrue (pm.getProduct(b1) != null);
		
		assertTrue (pm.deleteProduct(pm.getProduct(b1)));
		
		assertFalse (pm.getProduct(p1.getBarCode()) != null);
		assertTrue (pm.getProduct(p1.getBarCode()) == null);
		
		//assertTrue (pm.getAllProducts().size() == 0);
	}
	
	@Test
	public void testToString()
	{
		ProductManager pm = ProductManager.getInstance();
		Product p1 = createRandomProduct();
		Product p2 = createRandomProduct();
		Product p3 = createRandomProduct();
		Product p4 = createRandomProduct();
		
		assertTrue (pm.addProduct(p1) && pm.addProduct(p2) && 
					pm.addProduct(p3) && pm.addProduct(p4));
		
		System.out.println(pm.toString());
		
		assertTrue (pm.deleteProduct(p1) && pm.deleteProduct(p2) && 
				 	pm.deleteProduct(p3) && pm.deleteProduct(p4));
		
		//assertTrue (pm.getAllProducts().size() == 0);
	}
	
	@Test
	public void testAddPRoductToContainer()
	{
		ProductManager pm = ProductManager.getInstance();
		Product p1 = createRandomProduct();
		Product p2 = createRandomProduct();
		
		ProductContainer pc1 = new StorageUnit("myContainer");
		ProductContainer pc2 = new StorageUnit("DOOD");
		ProductContainer pc3 = new StorageUnit("myC2");
		ProductContainer pc4 = new StorageUnit("myc3");
		
		
		
		assertTrue (pm.addProduct(p1) && pm.addProduct(p2));
		assertTrue (pc1 != null);
		
		assertTrue (pm.addProductToContainer(p1, pc1));
		assertTrue (pm.addProductToContainer(p1, pc2));
		assertTrue (pm.addProductToContainer(p1, pc3));
		assertTrue (pm.addProductToContainer(p1, pc4));
		
		Set<ProductContainer> containers = pm.getAllProductContainers(p1);
		
		assertTrue (containers.size() == 4);
		
		assertTrue (pm.getAllProductsInContainer(pc4).size() == 1);
		assertTrue (pm.getAllProductsInContainer(pc3).size() == 1);
		assertTrue (pm.getAllProductsInContainer(pc2).size() == 1);
		assertTrue (pm.getAllProductsInContainer(pc1).size() == 1);
		
		assertTrue (pm.deleteProduct(p1));
		
		assertTrue (pm.deleteProduct(p2));
		
		assertTrue (pm.getAllProductContainers(p1) == null);
		
		//assertTrue (pm.getAllProductContainers(p1).size() == 0);
		assertTrue (pm.getAllProductsInContainer(pc4).size() == 0);
		assertTrue (pm.getAllProductsInContainer(pc3).size() == 0);
		assertTrue (pm.getAllProductsInContainer(pc2).size() == 0);
		assertTrue (pm.getAllProductsInContainer(pc1).size() == 0);
		
		//assertTrue (pm.getAllProducts().size() == 0);
		
		
		
	}
	
	@Test
	public void testGetaAllProductsInContainer()
	{
		ProductManager pm = ProductManager.getInstance();
		Product p1 = createRandomProduct();
		Product p2 = createRandomProduct();
		
		ProductContainer pc1 = new StorageUnit("myContainer");
		ProductContainer pc2 = new StorageUnit("DOOD");
		ProductContainer pc3 = new StorageUnit("myC2");
		ProductContainer pc4 = new StorageUnit("myc3");
		
		assertTrue (pm.addProduct(p1) && pm.addProduct(p2));
		assertTrue (pc1 != null);
		
		//assertTrue (pm.addProductToContainer(p1, pc1));
		assertTrue (pm.addProductToContainer(p1, pc2));
		assertTrue (pm.addProductToContainer(p1, pc3));
		assertTrue (pm.addProductToContainer(p1, pc4));
		
		assertTrue (pm.addProductToContainer(p2, pc2));
		assertTrue (pm.addProductToContainer(p2, pc3));
		
		assertTrue (pm.getAllProductsInContainer(pc4).size() == 1);
		assertTrue (pm.getAllProductsInContainer(pc3).size() == 2);
		assertTrue (pm.getAllProductsInContainer(pc2).size() == 2);
		assertTrue (pm.getAllProductsInContainer(pc1).size() == 1);
		
		Set<Product> pic = pm.getAllProductsInContainer(pc1);
		assertTrue (pic.contains(p1));
		
		pic = pm.getAllProductsInContainer(pc2);
		assertTrue (pic.contains(p1) && pic.contains(p2));
		
		pic = pm.getAllProductsInContainer(pc3);
		assertTrue (pic.contains(p1) && pic.contains(p2));
		
		assertTrue (pm.deleteProduct(p1) && pm.deleteProduct(p2));
		
		assertTrue (pm.getAllProductsInContainer(pc1).size() == 0);
		assertTrue (pm.getAllProductsInContainer(pc2).size() == 0);
		assertTrue (pm.getAllProductsInContainer(pc3).size() == 0);
		assertTrue (pm.getAllProductsInContainer(pc4).size() == 0);
		
		assertTrue (pm.deleteProductContainer(pc1));
		assertTrue (pm.deleteProductContainer(pc2));
		assertTrue (pm.deleteProductContainer(pc3));
		assertTrue (pm.deleteProductContainer(pc4));
		
		//assertTrue (pm.getAllProductsInContainer(pc1) == null);
		//assertTrue (pm.getAllProductsInContainer(pc2) == null);
		//assertTrue (pm.getAllProductsInContainer(pc3) == null);
		//assertTrue (pm.getAllProductsInContainer(pc4) == null);
		
		//assertTrue (pm.getAllProducts().size() == 2);
	}
	
	@Test
	public void testdeleteProductFromContainer()
	{
		ProductManager pm = ProductManager.getInstance();
		Product p1 = createRandomProduct();
		Product p2 = createRandomProduct();
		
		ProductContainer pc1 = new StorageUnit("myContainer");
		
		assertTrue (pm.addProduct(p1) && pm.addProduct(p2));
		
		assertTrue (pm.addProductToContainer(p1, pc1));
		assertTrue (pm.addProductToContainer(p2, pc1));
		
		assertTrue (pm.getAllProductsInContainer(pc1).size() == 2);
		
		Set<Product>pic = pm.getAllProductsInContainer(pc1);
		assertTrue (pic.contains(p1) && pic.contains(p2));
		
		assertTrue (pm.deleteProductFromContainer(p1, pc1));
		pic = pm.getAllProductsInContainer(pc1);
		assertTrue (pic != null);
		assertTrue (pic.size() == 1);
		assertFalse (pic.contains(p1));
		assertTrue (pic.contains(p2));
		
		assertTrue (pm.deleteProduct(p1) && pm.deleteProduct(p2));
		//assertTrue (pm.getAllProducts().size() == 0);
	}
	
	@Test
	public void testGetContainer()
	{	
		ProductManager pm = ProductManager.getInstance();
		Product p1 = createRandomProduct();
		Product p2 = createRandomProduct();
		
		assertTrue (pm.addProduct(p1) && pm.addProduct(p2));
		
		StorageUnit pc1 = new StorageUnit("myContainer");
		ProductGroup pg1 = new ProductGroup("myContainer2", new Amount(Unit.count, 2), pc1);
		
		pc1.addProductGroup(pg1);
		assertTrue (pc1.getProductGroups().size() == 1);
		
		assertTrue (pm.addProductToContainer(p1,  pg1));
		assertTrue (pm.addProductToContainer(p2,  pc1));
		
		ProductContainer pc = pm.getContainer(pc1, p2);
		assertTrue (pc != null);
		assertTrue (pc.getName() == "myContainer");
		
		assertTrue (pc1.getProductGroups().size() == 1);
		
		pc = pm.getContainer(pc1, p1);
		assertTrue (pc != null);
		assertTrue (pc.getName() == "myContainer2");
		
		assertTrue (pm.deleteProduct(p1) && pm.deleteProduct(p2));
		//assertTrue (pm.getAllProducts().size() == 0);
		
		assertTrue (pm.deleteProductContainer(pc1));
		assertTrue (pm.deleteProductContainer(pg1));
	}
}
